﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using PMS.Core.Infrastructure;
using PMS.Core.Utils.Authentication;
using PMS.Core.Utils.Http;
using PMS.Data.Entities.Settings;
using PMS.Services.SharedKernel;
using PMS.Services.Utils;
using System;
using System.Text;
using System.Threading.Tasks;

namespace PMS.Services.Authentication.Externel.JwtAuthRegistrar
{
    /// <summary>
    /// Represents registrar of Jwt authentication service
    /// </summary>
    public class JwtAuthenticationRegistrar : IExternalAuthenticationRegistrar
    {
        #region Utils
        protected virtual string GetCustomerClientCookie(HttpContext context)
        {
            var cookieName = $"{CookieDefaultConfigs.Prefix}{CookieDefaultConfigs.CustomerClienCookie}";
            return context?.Request?.Cookies[cookieName];
        }
        /// <summary>
        /// clear client jwt cookie
        /// </summary>
        protected virtual void ClearCustomerClientCookie(HttpContext context)
        {
            if (context?.Response == null)
                return;

            //delete current cookie value
            var cookieName = $"{CookieDefaultConfigs.Prefix}{CookieDefaultConfigs.CustomerClienCookie}";
            context?.Response.Cookies.Delete(cookieName);
        }
        #endregion
        #region Methods
        /// <summary>
        /// Configure
        ///https://blog.csdn.net/dieya9314/article/details/101748482
        ///https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs
        /// </summary>
        /// <param name="builder">Authentication builder</param>
        public void Configure(AuthenticationBuilder builder)
        {
            builder.AddJwtBearer(AuthenticationDefaultConfigs.JwtAuthenticationScheme, options =>
            {
                //set credentials
                var securitySettings = EngineContext.Current.Resolve<SecuritySettings>();
                //set credentials
                TimeSpan expiration = securitySettings.JwtExpirationMinutes <= 0
                                    ? TimeSpan.FromMinutes(Convert.ToDouble(AuthenticationDefaultConfigs.JwtExpirationMinutes))
                                    : TimeSpan.FromMinutes(Convert.ToDouble(securitySettings.JwtExpirationMinutes));
                var SecurityKey = string.IsNullOrEmpty(securitySettings.JwtIssuerSigningKey)
                                ? new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthenticationDefaultConfigs.JwtIssuerSigningKey))
                                : new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securitySettings.JwtIssuerSigningKey));
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,           //是否验证Issuer
                    ValidateAudience = true,         //是否验证Audience
                    ValidateLifetime = true,         //是否验证失效时间
                    ValidateIssuerSigningKey = true, //是否验证SecurityKey
                    ClockSkew = TimeSpan.Zero,
                    ValidAudience = string.IsNullOrEmpty(securitySettings.JwtValidAudience) //Audience
                                  ? AuthenticationDefaultConfigs.JwtValidAudience
                                  : securitySettings.JwtValidAudience,
                    ValidIssuer = string.IsNullOrEmpty(securitySettings.JwtValidIssuer)     //Issuer，这两项和前面签发jwt的设置一致
                                ? AuthenticationDefaultConfigs.JwtValidIssuer
                                : securitySettings.JwtValidIssuer,
                    IssuerSigningKey = SecurityKey                                  //拿到SecurityKey
                };
                options.SaveToken = false; //Store JwtToken within Cookie myself
                options.Events = new JwtBearerEvents()
                {
                    OnMessageReceived = context =>
                    {
                        context.Token = string.Empty;
                        var webHelper = EngineContext.Current.Resolve<IWebHelper>();
                        //AJAX WebApi 尝试用组件自带HandleAuthenticateAsync从Request.Headers获取JWTToken
                        if (!webHelper.IsAjaxRequest(context.Request))
                        {
                            string clientJwt = GetCustomerClientCookie(context.HttpContext);
                            if (!string.IsNullOrEmpty(clientJwt))
                                context.Token = clientJwt;
                        }
                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = context =>
                    {
                        var webHelper = EngineContext.Current.Resolve<IWebHelper>();
                        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        if (webHelper.IsAjaxRequest(context.Request))
                        {
                            context.Response.ContentType = "application/json";
                            string error = "您没有操作权限";
                            string error_description = "您当前访问页面需要操作授权,可能没有登录或授权验证失败";
                            // Add some extra context for expired tokens.
                            if (context.Exception != null && context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                var authenticationException = context.Exception as SecurityTokenExpiredException;
                                context.Response.Headers.Add("pms-token-expired", authenticationException.Expires.ToString("o"));
                                error_description = "您的操作授权已过期,请重新登录";//$"The token expired on {authenticationException.Expires.ToString("o")}";
                            }

                            return context.Response.WriteAsync(JsonConvert.SerializeObject(new
                            {
                                error,
                                error_description
                            }));
                        }
                        else
                        {
                            if (context.Exception != null && context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                var workContext = EngineContext.Current.Resolve<IWorkContext>();
                                if(workContext.IsAdminArea || workContext.IsGuestsArea)
                                    context.Response.Redirect(AuthenticationDefaultConfigs.ErrorPath + "?errmsg=tokenexpired", false);
                                ClearCustomerClientCookie(context.HttpContext);
                                return Task.FromResult(0);
                            }
                            context.Response.Redirect(AuthenticationDefaultConfigs.AccessDeniedPath);
                            ClearCustomerClientCookie(context.HttpContext);
                            return Task.CompletedTask;
                        }
                    }
                };
            });
        }
        #endregion
    }
}
